<?php

namespace App\Admin\Rules;

use App\Admin\Models\SystemModel;
use Illuminate\Contracts\Validation\Rule;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\File;

/**
 * 单选或者多选类型时，选项配置验证规则
 * 必须至少要有一个类似 1=>值 的值
 *
 * Class SelectTypeOptions
 * @package App\Admin\Rules
 */
class SelectTypeOptions implements Rule
{
    protected $errorMessage;
    
    protected $selectType = [
        'SIMPLE',
        'PRIMARY',
        'SUBQUERY',
        'DERIVED',
        'UNION',
        'UNION RESULT',
        'DEPENDENT UNION',
        'SUBQUERY',
        'DEPENDENT SUBQUERY'
    ];

    protected $option;

    public function __construct($option){
        $this->option = $option;
    }

    protected function checkSql($sql)
    {
        $sql = rtrim($sql, ';');
        try {
            $explainInfo = objToArr(DB::select('EXPLAIN '.$sql));
            if (!in_array($explainInfo[0]['select_type'], $this->selectType)) {
                return false;
            }
            $data = objToArr(DB::select($sql));
            if (!$data) {
                $this->errorMessage = '没有查询到选项数据。';
                return false;
            }
        } catch (\Exception $e) {
            $this->errorMessage = $e->getMessage();
            return false;
        }
        return true;
    }

    public function passes($attribute, $value)
    {
        // 为空不验证
        if (!$value) {
            return true;
        }
        if ($this->option['sample']==1) {
            $pairs = explode("\n", $value);
            foreach ($pairs as $pair) {
                $p = explode('=>', $pair);
                if (count($p) < 2) {
                    return false;
                }
                $label = $p[1];
                // label 有就表示有效，有一个有效，则可以通过
                if ($label) {
                    return true;
                }
            }
            return false;
        } elseif ($this->option['sample']==2) {
            if (!$this->checkSql($value)) {
                return false;
            }
            return true;
        } elseif ($this->option['sample']==3) {
            return true;
        }
        return false;
    }

    public function message()
    {
        return $this->errorMessage;
    }
}

class SelectTreeOptions implements Rule
{
    protected $errorMessage;
    
    protected $selectType = [
        'SIMPLE',
        'PRIMARY',
        'SUBQUERY',
        'DERIVED',
        'UNION',
        'UNION RESULT',
        'DEPENDENT UNION',
        'SUBQUERY',
        'DEPENDENT SUBQUERY'
    ];

    protected $option;

    public function __construct($option){
        $this->option = $option;
    }

    public function passes($attribute, $value)
    {
        // 为空不验证
        if (!$value) {
            return true;
        }
        if ($this->option['sample']==1) {
            if (!is_string($value)) {
                $this->errorMessage = 'JSON数据格式有误';
                return false;
            }
            $pairs = json_decode($value, true);
            if (!$pairs) {
                $this->errorMessage = 'JSON数据格式有误';
                return false;
            }
            // 遍历数组并检测格式
            foreach ($pairs as $item) {
                if (!$this->checkSelectTreeValue($item)) {
                    $this->errorMessage = '格式有误';
                    return false;
                }
            }
            return true;
        } elseif ($this->option['sample']==2) {
            if (!$this->checkSql($value)) {
                return false;
            }
            return true;
        } 
        return true;
    }

    // 递归函数，用于检测多级联动数组格式是否正确
    public function checkSelectTreeValue($arr) {
        // 检查数组是否是一个有效的数组
        if (!is_array($arr)) {
            return false;
        }
        // 检查数组是否包含必需的键
        if (!isset($arr['value']) || !isset($arr['label'])) {
            return false;
        }
        // 如果有 children 键，递归检查 children 数组
        if (isset($arr['children']) && is_array($arr['children'])) {
            foreach ($arr['children'] as $child) {
                if (!$this->checkSelectTreeValue($child)) {
                    return false;
                }
            }
        }
        // 数组格式正确
        return true;
    }

    protected function checkSql($value)
    {
        try {
            $data = objToArr(DB::select($value));
        } catch (\Illuminate\Database\QueryException $e) {
            $this->errorMessage = $e->getMessage();
            return false;
        }
        if (!$data) {
            $this->errorMessage = '没有查询到选项数据。';
            return false;
        }
        // 遍历数组并检测格式
        foreach ($data as $item) {
            if (!$this->checkSelectTreeValue($item)) {
                $this->errorMessage = '格式有误，必须包含value、label两个字段';
                return false;
            }
        }
        return true;
    }

    public function message()
    {
        return $this->errorMessage;
    }
}

class CascaderOptions implements Rule
{
    protected $errorMessage;

    protected $selectType = [
        'SIMPLE',
        'PRIMARY',
        'SUBQUERY',
        'DERIVED',
        'UNION',
        'UNION RESULT',
        'DEPENDENT UNION',
        'SUBQUERY',
        'DEPENDENT SUBQUERY'
    ];

    protected $option;

    public function __construct($option){
        $this->option = $option;
    }

    public function passes($attribute, $value)
    {
        // 为空不验证
        if (!$value) {
            return true;
        }
        if ($this->option['sample']==1) {
            if (!is_string($value)) {
                $this->errorMessage = 'JSON数据格式有误';
                return false;
            }
            $pairs = json_decode($value, true);
            if (!$pairs) {
                $this->errorMessage = 'JSON数据格式有误';
                return false;
            }
            // 遍历数组并检测格式
            foreach ($pairs as $item) {
                if (!$this->checkCascaderValue($item)) {
                    $this->errorMessage = '格式有误';
                    return false;
                }
            }
            return true;
        } elseif ($this->option['sample']==2) {
            if (!$this->checkSql($value)) {
                return false;
            }
            return true;
        } elseif ($this->option['sample']==4) {
            $file = public_path($value['path']);
            if (!File::exists($file)) {
                $this->errorMessage = '文件不存在';
                return false;
            }
            $pairs = json_decode(File::get($file), true);
            if (!$pairs) {
                $this->errorMessage = 'JSON数据格式有误';
                return false;
            }
           
            // 遍历数组并检测格式
            foreach ($pairs as $item) {
                if (!$this->checkCascaderValue($item)) {
                    $this->errorMessage = '格式有误';
                    return false;
                }
            }
        }
        return true;
    }

    public function message()
    {
        return $this->errorMessage;
    }

    // 递归函数，用于检测多级联动数组格式是否正确
    public function checkCascaderValue($arr) {
        // 检查数组是否是一个有效的数组
        if (!is_array($arr)) {
            return false;
        }
        // 检查数组是否包含必需的键
        if (!isset($arr['value']) || !isset($arr['label'])) {
            return false;
        }
        // 如果有 children 键，递归检查 children 数组
        if (isset($arr['children']) && is_array($arr['children'])) {
            foreach ($arr['children'] as $child) {
                if (!$this->checkCascaderValue($child)) {
                    return false;
                }
            }
        }
        // 数组格式正确
        return true;
    }

    protected function checkSql($value)
    {
        if (!is_string($value)) {
            $this->errorMessage = 'SQL语句格式错误';
            return false; 
        }
        try {
            $data = objToArr(DB::select($value));
        } catch (\Illuminate\Database\QueryException $e) {
            $this->errorMessage = $e->getMessage();
            return false;
        }
        if (!$data) {
            $this->errorMessage = '没有查询到选项数据。';
            return false;
        }
        // 遍历数组并检测格式
        foreach ($data as $item) {
            if (!isset($item['parent_id'])) {
                $this->errorMessage = '格式有误，必须包含parent_id字段';
                return false;
            }
            if (!$this->checkCascaderValue($item)) {
                $this->errorMessage = '格式有误，必须包含value、label两个字段';
                return false;
            }
        }
        return true;
    }
}

class Options implements Rule
{
    /**
     * @var string 配置类型
     */
    protected $type;
    protected $errorMessage;

    /**
     * Create a new rule instance.
     *
     * @param string $type
     *
     * @return void
     */
    public function __construct(string $type = null)
    {
        $this->type = $type;
    }

    /**
     * Determine if the validation rule passes.
     *
     * @param string $attribute
     * @param mixed $value
     *
     * @return bool
     */
    public function passes($attribute, $value)
    {
        $typeMap = array_merge(SystemModel::$inputTypeMap, SystemModel::$searchTypeMap);
        // 如果 type 类型不对，则不用验证
        if (!isset($typeMap[$this->type])) {
            return true;
        }

        switch ($this->type) {
            case SystemModel::TYPE_INPUT:
            case SystemModel::TYPE_TEXTAREA:
            case SystemModel::TYPE_OTHER:
                !is_null($value) && ($this->errorMessage = '选项 必须为空。');
                break;
            case SystemModel::TYPE_FILE:
                if (!is_array($value)) {
                    $this->errorMessage = '选项 无效。';
                    break;
                }

                $validator = Validator::make($value, [
                    'max' => 'required|integer|between:1,99',
                    'ext' => 'nullable',
                ], [], [
                    'max' => '最大上传数',
                    'ext' => '文件类型',
                ]);
                break;
            case SystemModel::TYPE_SINGLE:
            case SystemModel::TYPE_MULTIPLE:
                if (!is_array($value)) {
                    $this->errorMessage = '选项 无效。';
                    break;
                }
               
                break;
            case SystemModel::TYPE_SELECT_TREE:
                if (!is_array($value)) {
                    $this->errorMessage = '选项 无效。';
                    break;
                }
                $validator = Validator::make($value, [
                    'options' => ['required', new SelectTreeOptions($value)],
                ], [], [
                    'options' => '选项设置',
                    'type' => '选择形式',
                ]);
                break;
            case SystemModel::TYPE_CASCADER:
                if (!is_array($value)) {
                    $this->errorMessage = '选项 无效。';
                    break;
                }
                $validator = Validator::make($value, [
                    'options' => ['required', new CascaderOptions($value)],
                ], [], [
                    'options' => '选项设置',
                    'type' => '选择形式',
                ]);
                break;
            default:
                // do nothing
        }

        if (isset($validator) && $validator->fails()) {
            $this->errorMessage = $validator->getMessageBag()->first();
        }

        return !$this->errorMessage;
    }

    /**
     * Get the validation error message.
     *
     * @return string
     */
    public function message()
    {
        return $this->errorMessage;
    }
}
